
mmu_error
translate_fixed(HOST *h, sim_addr vaddr, sim_addr *addr, mmu_prot *prot) {
	
	if(vaddr <= REGION_END_KUSEG) {
		// bottom 1024 MB of memory reserved for kernel
		vaddr += 0x40000000UL;
		*prot = PROT_USER | PROT_READ | PROT_WRITE | PROT_CACHED | PROT_MAPPED;
	}
#ifdef MIPS64
	else if(vaddr <= REGION_END_XSSEG) {
		vaddr += 0x40000000UL;
		*prot = PROT_SUPERVISOR | PROT_READ | PROT_WRITE | PROT_CACHED | PROT_MAPPED;
	}
	else if(vaddr <= REGION_END_CWIN) {
		vaddr -= REGION_START_CWIN;
		*prot = PROT_KERNEL | PROT_READ | PROT_WRITE | PROT_CACHED;
	}
	else if(vaddr <= REGION_END_UWIN) {
		vaddr -= REGION_START_UWIN;
		*prot = PROT_KERNEL | PROT_READ | PROT_WRITE;
	}
	else if(vaddr <= REGION_END_XKSEG) {
		vaddr += 0x40000000UL;
		*prot = PROT_KERNEL | PROT_READ | PROT_WRITE | PROT_CACHED | PROT_MAPPED;
	}
#endif /* MIPS64 */
	else if(vaddr <= REGION_END_KSEG0) {
		vaddr -= REGION_START_KSEG0;
		*prot = PROT_KERNEL | PROT_READ | PROT_WRITE | PROT_CACHED;
	}
	else if(vaddr <= REGION_END_KSEG1) {
		vaddr -= REGION_END_KSEG1;
		*prot = PROT_KERNEL | PROT_READ | PROT_WRITE;
	}
	else if(vaddr <= REGION_END_SSEG0) {
		*prot = PROT_SUPERVISOR | PROT_READ | PROT_WRITE | PROT_CACHED | PROT_MAPPED;
	}
	else if(vaddr <= REGION_END_KSEG2) {
		*prot = PROT_KERNEL | PROT_READ | PROT_WRITE | PROT_CACHED | PROT_MAPPED;
	}

	*addr = vaddr;
	return E_OKAY;
}

mmu_error
translate_tlb(HOST *h, sim_addr vaddr, sim_addr *addr, mmu_prot *prot) {
	sim_addr f_addr;
	mmu_error merr;
	tlb_error terr;
	sim_size asid;
	tlb_paddr tflags;

	if(merr = translate_fixed(h, vaddr, &f_addr, prot))
		return merr;

	if(!(prot & PROT_MAPPED)) {
		*addr = f_addr;
		return E_OKAY;
	}

	if(!(h->mmu->tlb)) {
		return E_MMU_NOTLB;
	}
	
	// We need the address space ID
	asid = get_asid(h);
	if(terr = tlb_lookup(h->mmu->tlb, vaddr, asid, addr, &tflags))
		return terr;
	
	if((tflags & PFN_CACHE) == F_TLB_NOCACHE)
		*prot &= ~PROT_CACHED;
	
	if((tflags & PFN_CACHE) == F_TLB_NOCOHERENT)
		*prot |= PROT_NOCOHERENT;
	
	if(tflags & PFN_DIRTY)
		*prot |= PROT_WRITE;
	else
		*flags &= ~PROT_WRITE;
	
	if(tflags & PFN_VALID)
		*prot |= PROT_READ;
	else
		*prot &= ~(PROT_READ | PROT_WRITE);

	return E_MMU_TLB;
}

mmu_error
translate_address(HOST *h, map_type map, sim_addr vaddr, sim_addr *addr, mmu_prot *prot) {
	if(!map)
		map = get_map(h, src);
	
	switch(map) {
	case MAP_NONE:
		*addr = vaddr;
		*prot = PROT_READ | PROT_WRITE;
		return E_OKAY;
	case MAP_FIXED:
		return translate_fixed(h, vaddr, addr, prot);
	case MAP_TLB:
		return translate_tlb(h, vaddr, addr, prot);
	case MAP_ECC:
		// During an ECC exception we always define a static uncached map over the low memory as follows
		// this is so an ECC vector can use off($0) to store k0/k1 registers
		*addr = vaddr;
		flags = PROT_KERNEL | PROT_READ | PROT_WRITE;
		return E_OKAY;
	}

	return E_INVAID;
}

mmu_error
locate_address(HOST *h, sim_addr addr, mmu_target *dest, mmu_prot *prot, mmu_dev **device) {
	mmu_dev dev;
	*prot = 0;

	dev = h->mmu->devs;
	while(dev) {
		if(addr >= dev->start && addr <= dev->end) {
			*device = dev;
			*dest = TARGET_DEV;
			if(dev->read)	*prot |= PROT_READ;
			if(dev->write)	*prot |= PROT_WRITE;
			return E_OKAY;
		}
	}
	
	if(addr >= h->mmu->rom_start && addr <= h->mmu->rom_end) {
		*dest = TARGET_ROM;
		*prot = PROT_READ;
		return E_OKAY;
	}
	
	if(addr >= h->mmu->ram_start && addr <= h->mmu->ram_end) {
		*dest = TARGET_RAM;
		*prot = PROT_READ | PROT_WRITE;
		return E_OKAY;
	}

	return E_INVALID;
}

